﻿(function () {
    var YahooFinanceDataAdapter = WinJS.Class.define(
            function (symbol, startDate, endDate) {
                this.symbol = symbol;

                if (startDate) {
                    this.startDate = new Date(startDate);
                }

                if (endDate) {
                    if (Date.isPrototypeOf(endDate)) {
                        this.endDate = endDate;
                    } else if (String.isPrototypeOf(endDate)) {
                        this.endDate = new Date(endDate);
                    }
                }

                //this._requestResults();
            },
            {
                getCount: function () {
                    var that = this;
                    return that._requestResults().then(
                            function (results) {
                                return results.length;
                            }
                        );
                },

                itemsFromStart: function (count) {
                    return this.itemsFromIndex(0, 0, count - 1);
                },

                itemsFromIndex: function (requestIndex, countBefore, countAfter) {
                    var that = this;
                    return that._requestResults().then(
                        function (results) {
                            var startIndex = Math.max(0, requestIndex - countBefore);
                            var endIndex = Math.min(results.length - 1, requestIndex + countAfter + 1);
                            var subSet = results.slice(startIndex, endIndex);

                            var atStart = (startIndex == 0);
                            var atEnd = (endIndex == results.length - 1);

                            var output = {
                                items: subSet,
                                absoluteIndex: requestIndex,
                                offset: requestIndex - startIndex,
                                totalCount: subSet.length,
                                atStart: atStart,
                                atEnd: atEnd
                            };

                            return output;
                        }
                    );
                },

                _results: null,

                _worker: null,

                _messageResponse: function (message, complete, error) {
                    var data = message.data;

                    if (data.type == "price_history") {
                        _results = data.stocks;
                        complete(data.stocks);
                    }
                },

                _getUri: function (symbol, startDate, endDate) {
                    var uri = "http://ichart.finance.yahoo.com/table.csv";
                    uri += "?s=" + symbol;

                    if (startDate) {
                        uri += "&a=" + startDate.getUTCMonth();
                        uri += "&b=" + startDate.getUTCDate();
                        uri += "&c=" + startDate.getUTCFullYear();
                    }

                    if (endDate) {
                        uri += "&d=" + endDate.getUTCMonth();
                        uri += "&e=" + endDate.getUTCDate();
                        uri += "&f=" + endDate.getUTCFullYear();
                    }

                    uri += "&g=d";

                    return uri;
                },

                _requestResults: function () {
                    var that = this;
                    return new WinJS.Promise(
                        function (complete, error) {
                            if (that._results) complete(that._results);
                                //else if(that._worker) {

                                //    that._worker.addEventListener('message', function(message) {
                                //        that._messageResponse(message, complete, error);
                                //    }, false);

                                //    that._worker.addEventListener('error', function (err) {
                                //        error(err);
                                //    });

                                //} 
                            else {

                                WinJS.xhr({
                                    type: "GET",
                                    url: that._getUri(that.symbol, that.startDate, that.endDate)
                                }).done(
                                        function (request) {

                                            if (that._results) complete(that._results);
                                            else {
                                                var response = request.responseText;

                                                var lines = response.split('\n');

                                                var myStocks = [];

                                                for (var i = lines.length - 1; i > 0; i--) {
                                                    var values = lines[i].split(',');

                                                    if (values.length == 7) {
                                                        var stock = new FolioFixer.DataTypes.AssetPriceItem(values, that.symbol);
                                                        myStocks.push(stock);
                                                    }
                                                }

                                                myStocks = _.sortBy(myStocks, function (stock) { return stock.date; });

                                                that._results = myStocks;

                                                complete(myStocks);
                                            }
                                        }
                                    );
                            }
                        }
                    );
                },


            });

    var YahooDataSource = WinJS.Class.derive(
        WinJS.UI.VirtualizedDataSource,
        function (symbol, startDate, endDate) {
            this._adapter = new YahooFinanceDataAdapter(symbol, startDate, endDate);
            this._baseDataSourceConstructor(this._adapter);
        }
    );

    var IndexedDbDataAdapter = WinJS.Class.define(
        function (dbName, dbVersion, objectStoreName, upgrade, error) {
            this._dbName = dbName;
            this._dbVersion = dbVersion;
            this._objectStoreName = objectStoreName;
            this._upgrade = upgrade;
            this._error = error || function (evt) { console.log(evt.message); };
        },
        {
            /* IListDataAdapter Interface Methods */

            getCount: function () {
                var that = this;
                return new WinJS.Promise(function (complete, error) {
                    that._getObjectStore().then(function (store) {
                        var reqCount = store.count();
                        reqCount.onerror = that._error;
                        reqCount.onsuccess = function (evt) {
                            complete(evt.target.result);
                        };
                    });
                });
            },

            itemsFromIndex: function (requestIndex, countBefore, countAfter) {
                var that = this;
                return new WinJS.Promise(function (complete, error) {
                    that.getCount().then(function (count) {
                        if (requestIndex >= count) {
                            return WinJS.Promise.wrapError(new WinJS.ErrorFromName(WinJS.UI.FetchError.doesNotExist));
                        }
                        var startIndex = Math.max(0, requestIndex - countBefore);
                        var endIndex = Math.min(count, requestIndex + countAfter + 1);

                        that._getObjectStore().then(function (store) {
                            var index = 0;
                            var items = [];
                            var req = store.openCursor();
                            req.onerror = that._error;
                            req.onsuccess = function (evt) {
                                var cursor = evt.target.result;

                                if (index < startIndex) {
                                    index = startIndex;
                                    cursor.advance(startIndex);
                                    return;
                                }

                                if (cursor && index < endIndex) {
                                    index++;
                                    items.push({
                                        key: cursor.value[store.keyPath].toString(),
                                        data: cursor.value
                                    });
                                    cursor.continue();
                                    return;
                                }

                                results = {
                                    items: items,
                                    offset: requestIndex - startIndex,
                                    totalCount: count
                                };
                                complete(results);
                            };
                        });
                    });
                });
            },

            insertAtEnd: function (unused, data) {
                var that = this;
                return new WinJS.Promise(function (complete, error) {
                    var oCount;
                    that.getCount().then(function (count) {
                        oCount = count;
                        return that._getObjectStore("readwrite");
                    }).done(function (store) {
                        data.id = oCount;
                        var reqAdd = store.add(data);
                        reqAdd.onerror = that._error;
                        reqAdd.onsuccess = function (evt) {
                            var reqGet = store.get(evt.target.result);


                            reqGet.onerror = that._error;
                            reqGet.onsuccess = function (evt) {
                                var newItem = {
                                    key: evt.target.result[store.keyPath].toString(),
                                    data: evt.target.result
                                }

                                that._notificationHandler.inserted(newItem, newItem.key - 1, newItem.key + 1, newItem.key - 1);

                                complete(newItem);
                            };
                        };
                    });
                });
            },

            setNotificationHandler: function (notificationHandler) {
                this._notificationHandler = notificationHandler;
            },

            /* IndexedDbDataSource method */

            removedInternal: function (key) {
                var that = this;
                return new WinJS.Promise(function (complete, error) {
                    that._getObjectStore("readwrite").done(function (store) {
                        var reqDelete = store.delete(key);
                        reqDelete.onerror = that._error;
                        reqDelete.onsuccess = function (evt) {
                            that._notificationHandler.removed(key.toString());
                            complete();
                        };
                    });
                });
            },

            nuke: function () {
                var that = this;
                return new WinJS.Promise(function (complete, error) {
                    that._getObjectStore("readwrite").done(function (store) {
                        var reqClear = store.clear();
                        reqClear.onerror = that._error;
                        reqClear.onsuccess = function (evt) {
                            that._notificationHandler.reload();
                            complete();
                        };
                    });
                });
            },

            getFromKey: function (key) {
                var that = this;
                return new WinJS.Promise(function (complete, error) {
                    that._getObjectStore().done(function (store) {
                        var reqGet = store.get(key);
                        reqGet.onerror = that._error;
                        reqGet.onsuccess = function (item) {
                            complete(item);
                        }
                    });
                });
            },

            put: function (data) {
                var that = this;
                return new WinJS.Promise(function (complete, error) {
                    that._getObjectStore("readwrite").done(function (store) {

                        var reqPut = store.put(data);

                        reqPut.onerror = that._error;
                        reqPut.onsuccess = function () {
                            complete();
                        }
                    });
                });
            },

            /* Private Methods */

            _ensureDbOpen: function () {
                var that = this;

                if (that._cachedDb) {
                    return WinJS.Promise.wrap(that._cachedDb);
                }

                return new WinJS.Promise(function (complete, error, progress) {
                    var reqOpen = window.indexedDB.open(that._dbName, that._dbVersion);
                    reqOpen.onerror = function (evt) {
                        error();
                    };

                    reqOpen.onupgradeneeded = function (evt) {
                        that._upgrade(evt);
                        that._notificationHandler.invalidateAll();
                    };

                    reqOpen.onsuccess = function () {
                        that._cachedDb = reqOpen.result;
                        complete(that._cachedDb);
                    };
                });
            },

            _getObjectStore: function (type) {
                type = type || "readonly";
                var that = this;
                return new WinJS.Promise(function (complete, error) {
                    that._ensureDbOpen().then(function (db) {
                        var transaction = db.transaction(that._objectStoreName, type);
                        complete(transaction.objectStore(that._objectStoreName));
                    });
                });
            }
        }
    );

    var IndexedDbDataSource = new WinJS.Class.derive(
        WinJS.UI.VirtualizedDataSource,
        function (dbVersion, objectStoreName, error) {

            this._upgrade = function (evt) {
                db = evt.target.result;

                if (!(_.contains(db.objectStoreNames, "portfolios"))) {
                    var portfolioStore = db.createObjectStore("portfolios", { keyPath: "id", autoIncrement: true });
                    //var dp = new FolioFixer.DataTypes.PortfolioInfo("Big Four", new FolioFixer.DataTypes.Date(2008, 8, 10), ["MSFT", "AAPL", "GOOG", "FB"]);
                    //portfolioStore.put(dp);
                }
            }

            this._adapter = new IndexedDbDataAdapter("db", dbVersion, objectStoreName, this._upgrade, error);
            this._baseDataSourceConstructor(this._adapter);
        },
        {
            nuke: function () {
                return this._adapter.nuke();
            },

            remove: function (key) {
                return this._adapter.removeInternal(key);
            },

            add: function (obj) {
                return this._adapter.insertAtEnd(null, obj);
            },

            getFromKey: function (key) {
                return this._adapter.getFromKey(key);
            },

            put: function (data) {
                return this._adapter.put(data);
            }
        }
    );

    WinJS.Namespace.define("FolioFixer.DataSources", {
        PortfolioDataSource: PortfolioLocalDataSource,
        AssetDataSource: AssetDataSource
    });
})();